//SPDX-FileCopyrightText: Copyright 2025-2025 深圳市同心圆网络有限公司
//SPDX-License-Identifier: GPL-3.0-only

package user_dao

import (
	"context"
	"regexp"

	"gitcode.com/eteam/api-server/dao/common"
	"gitcode.com/eteam/proto-gen-go.git/user_api"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type BasicUserInfoDbItem struct {
	DisplayName string `bson:"displayName"`
	LogoUri     string `bson:"logoUri"`
}

type UserInfoDbItem struct {
	UserId     string              `bson:"_id"`
	UserName   string              `bson:"userName"`
	BasicInfo  BasicUserInfoDbItem `bson:"basicInfo"`
	CreateTime int64               `bson:"createTime"`
	UpdateTime int64               `bson:"updateTime"`
	UserState  user_api.USER_STATE `bson:"userState"`
}

func (item *UserInfoDbItem) ToUserInfo() *user_api.UserInfo {
	return &user_api.UserInfo{
		UserId:   item.UserId,
		UserName: item.UserName,
		BasicInfo: &user_api.BasicUserInfo{
			DisplayName: item.BasicInfo.DisplayName,
			LogoUri:     item.BasicInfo.LogoUri,
		},
		CreateTime: item.CreateTime,
		UpdateTime: item.UpdateTime,
		UserState:  item.UserState,
	}
}

type _UserInfoDao struct {
	mc *mongo.Collection
}

func newUserInfoDao(db *mongo.Database) (*_UserInfoDao, error) {
	mc := db.Collection(common.DB_NS_USER + "info")
	_, err := mc.Indexes().CreateMany(context.Background(), []mongo.IndexModel{
		{
			Keys:    bson.M{"userName": 1},
			Options: options.Index().SetUnique(true),
		},
		{
			Keys: bson.M{"createTime": 1},
		},
	})
	if err != nil {
		return nil, err
	}
	return &_UserInfoDao{mc: mc}, nil
}

func (db *_UserInfoDao) ExistByName(ctx context.Context, userName string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"userName": userName})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_UserInfoDao) ExistById(ctx context.Context, userId string) (bool, error) {
	count, err := db.mc.CountDocuments(ctx, bson.M{"_id": userId})
	if err != nil {
		return false, err
	}
	return count > 0, nil
}

func (db *_UserInfoDao) Insert(ctx context.Context, dbItem *UserInfoDbItem) error {
	_, err := db.mc.InsertOne(ctx, dbItem)
	return err
}

func (db *_UserInfoDao) GetByName(ctx context.Context, userName string) (*UserInfoDbItem, error) {
	retItem := &UserInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"userName": userName}).Decode(retItem)
	return retItem, err
}

func (db *_UserInfoDao) ListByName(ctx context.Context, userNameList []string) ([]*UserInfoDbItem, error) {
	if len(userNameList) == 0 {
		return []*UserInfoDbItem{}, nil
	}
	cursor, err := db.mc.Find(ctx, bson.M{"userName": bson.M{"$in": userNameList}})
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	return retList, err
}

func (db *_UserInfoDao) UpdateBasicInfo(ctx context.Context, userId string, basicDbItem *BasicUserInfoDbItem) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"basicInfo": basicDbItem}})
	return err
}

func (db *_UserInfoDao) Get(ctx context.Context, userId string) (*UserInfoDbItem, error) {
	retItem := &UserInfoDbItem{}
	err := db.mc.FindOne(ctx, bson.M{"_id": userId}).Decode(retItem)
	return retItem, err
}

func (db *_UserInfoDao) ListById(ctx context.Context, userIdList []string) ([]*UserInfoDbItem, error) {
	if len(userIdList) == 0 {
		return []*UserInfoDbItem{}, nil
	}
	cursor, err := db.mc.Find(ctx, bson.M{"_id": bson.M{"$in": userIdList}})
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) CountAll(ctx context.Context, filterByState bool, state uint32) (uint32, error) {
	query := bson.M{}
	if filterByState {
		query["userState"] = state
	}
	count, err := db.mc.CountDocuments(ctx, query)
	return uint32(count), err
}

func (db *_UserInfoDao) ListAll(ctx context.Context, filterByState bool, state uint32, offset, limit int64) ([]*UserInfoDbItem, error) {
	query := bson.M{}
	if filterByState {
		query["userState"] = state
	}
	cursor, err := db.mc.Find(ctx, query, options.Find().SetSort(bson.M{"createTime": -1}).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) CountByAdminApi(ctx context.Context,
	filterByKeyword bool, keyword string,
	filterByState bool, state uint32) (uint32, error) {
	condList := []bson.M{}
	if filterByKeyword {
		regFilter := primitive.Regex{
			Pattern: regexp.QuoteMeta(keyword),
			Options: "i",
		}
		condList = append(condList, bson.M{"$or": []bson.M{
			{"basicInfo.displayName": regFilter},
			{"userName": regFilter},
		}})
	}
	if filterByState {
		condList = append(condList, bson.M{"userState": state})
	}

	count, err := db.mc.CountDocuments(ctx, bson.M{"": condList})
	return uint32(count), err
}

func (db *_UserInfoDao) ListByAdminApi(ctx context.Context,
	filterByKeyword bool, keyword string,
	filterByState bool, state uint32,
	offset, limit int64) ([]*UserInfoDbItem, error) {
	condList := []bson.M{}
	if filterByKeyword {
		regFilter := primitive.Regex{
			Pattern: regexp.QuoteMeta(keyword),
			Options: "i",
		}
		condList = append(condList, bson.M{"$or": []bson.M{
			{"basicInfo.displayName": regFilter},
			{"userName": regFilter},
		}})
	}
	if filterByState {
		condList = append(condList, bson.M{"userState": state})
	}

	cursor, err := db.mc.Find(ctx, bson.M{"$and": condList}, options.Find().SetSort(bson.M{"createTime": -1}).SetSkip(offset).SetLimit(limit))
	if err != nil {
		return nil, err
	}
	retList := []*UserInfoDbItem{}
	err = cursor.All(ctx, &retList)
	if err != nil {
		return nil, err
	}
	return retList, nil
}

func (db *_UserInfoDao) UpdateUserState(ctx context.Context, userId string, userState uint32) error {
	_, err := db.mc.UpdateByID(ctx, userId, bson.M{"$set": bson.M{"userState": userState}})
	return err
}
